import { _decorator, AnimationComponent, CameraComponent, Component, find, Node, ParticleSystemComponent, SkeletalAnimationComponent, Vec3 } from 'cc';
import { mapManager } from './mapManager';
import { gameCamera } from './gameCamera';
import { constant } from '../framework/constant';
import { clientEvent } from '../framework/clientEvent';
import { player } from './player';
import { uiManager } from '../framework/uiManager';
import { playerData } from '../framework/playerData';
import { localConfig } from '../framework/localConfig';
import { audioManager } from '../framework/audioManager';
import { resourceUtil } from '../framework/resourceUtil';
import { poolManager } from '../framework/poolManager';
import { monster } from './monster';
import { boss } from './boss';
import { effectManager } from '../framework/effectManager';
const { ccclass, property } = _decorator;

@ccclass('gameManager')
export class gameManager extends Component {
    @property(gameCamera)
    /** 相机组件 */
    public camera: gameCamera = null;

    @property({ type: mapManager })
    /** 地图脚本组件 */
    public scriptMapManager: mapManager = null;

    @property(Node)
    /** 主光源 */
    public ndLight: Node = null;

    /** 地图信息 */
    public mapInfo: any = {};

    /** 相机组件 */
    public static mainCamera: CameraComponent | null = null;
    /** boss脚本 */
    public static scriptBoss: boss = null!;

    /** 游戏是否开始 */
    public static isGameStart: boolean = false;
    /** 游戏是否暂停 */
    public static isGamePause: boolean = false;
    /** 游戏是否结束 */
    public static isGameOver: boolean = false;
    /** 玩家是否复活 */
    public static isRevive: boolean = false;
    /** 小怪、boss数组 */
    public static arrMonster: Node[] = [];
    /** 游戏速度 */
    private static _gameSpeed: number = 1;
    /** 游戏全局管理节点 */
    public static ndGameManager: Node;
    /** 特效节点 */
    public static ndEffectManager: Node = null!;
    /** 小怪节点 */
    public static ndBoss: Node = null!;
    /**
     * 本层加载的npc、大爱心现存数量
     * 怪物不会和npc、大爱心同时出现，配置表格需注意
    */
    public static existentNum: number = 0;


    /** 玩家脚本 */
    public static scriptPlayer: player = null!;
    /** 相机脚本 */
    public static scriptGameCamera: gameCamera;
    /** 玩家节点 */
    public static ndPlayer: Node = null!;
    /** 是否首次加载 */
    public static isFirstLoad: boolean = false;
    /** 地图节点 */
    public static ndMapManager: Node = null!;



    /** 本层敌人加成 */
    /** 本层敌人攻击加成 */
    public static attackAddition: number = 1;
    /** 本层敌人防御加成 */
    public static defenseAddition: number = 1;
    /** 本层敌人生命加成 */
    public static hpAddition: number = 1;
    /** 本层敌人移速加成 */
    public static moveSpeedAddition: number = 1;
    /** 本层敌人攻速加成 */
    public static attackSpeedAddition: number = 1;
    /** 是否开启测试代码 */
    public static isTesting: boolean = true;

    /** 待加载的模块种类 */
    public _dictModuleType: any;
    /** 加载完成回调 */
    private _completeListener: Function = null!;
    /** 存放各项模块节点信息, 除了道路,在玩家后面一定距离则进行回收 */
    private _arrItem: any = [];
    /** 当前关卡数据表 */
    private _arrMap: any = [];
    /** 默认暗夜地图节点 */
    private _ndAn: Node = null!;
    /** S型暗夜地图节点 */
    private _ndAnS: Node = null!;
    /** 云 */

    /** 主光源节点初始世界坐标 */
    private _oriMainLightWorPos: Vec3 = null!;
    /** 主光源和玩家的向量差 */
    private _offsetWorPosMainLight: Vec3 = new Vec3();

    /** 是否取得胜利 */
    private static _isWin: boolean = false;
    /** 修改是否取得胜利 */
    public static set isWin(value: boolean) {
        this._isWin = value;

        if (gameManager.isGameStart) {
            clientEvent.dispatchEvent(constant.EVENT_TYPE.ON_GAME_OVER);
        }
    }
    /** 获取是否取得胜利 */
    public static get isWin() {
        return this._isWin;
    }

    /** 获取移动速度 */
    public static get gameSpeed() {
        return this._gameSpeed;
    }
    /** 修改移动速度 */
    public static set gameSpeed(value: number) {
        this._gameSpeed = value;
        // 刷新自节点的动画、粒子播放速度(游戏全局管理节点)
        gameManager.refreshEffectSpeed(gameManager.ndGameManager, this._gameSpeed);
        // 刷新自节点的动画、粒子播放速度(玩家节点)
        gameManager.refreshEffectSpeed(gameManager.ndPlayer as Node, this._gameSpeed);
        // 刷新自节点的动画、粒子播放速度(特效节点)
        gameManager.refreshEffectSpeed(gameManager.ndEffectManager as Node, this._gameSpeed);
    };

    /**
     * onEnable回调触发方式：
     * 1.当组件enabled(启用状态)从false变为true；
     * 2.所在节点的active属性从false变为true时；
     * 3.倘若节点第一次被创建且enabled(启用状态)为true，则会在onLoad之后。
     */
    onEnable() {
        /** 监听游戏初始化 */
        clientEvent.on(constant.EVENT_TYPE.ON_GAME_INIT, this._onGameInit, this);
        /** 监听游戏结束 */
        clientEvent.on(constant.EVENT_TYPE.ON_GAME_OVER, this._onGameOver, this);
        /** 监听游戏暂停 */
        clientEvent.on(constant.EVENT_TYPE.ON_GAME_PAUSE, this._onGamePause, this);
        /** 监听刷新关卡 */
        clientEvent.on(constant.EVENT_TYPE.REFRESH_LEVEL, this._refreshLevel, this);
        /** 监听回收游戏模型、特效 */
        clientEvent.on(constant.EVENT_TYPE.RECYCLE_ALL, this._recycleAll, this);
    }
    /**
     * onDisable回调触发方式：
     * 1.当组件enabled(启用状态)从true变为false；
     * 2.所在节点的active属性从true变为false时。
     */
    onDisable() {
        /** 取消监听游戏初始化 */
        clientEvent.off(constant.EVENT_TYPE.ON_GAME_INIT, this._onGameInit, this);
        /** 监听游戏结束 */
        clientEvent.off(constant.EVENT_TYPE.ON_GAME_OVER, this._onGameOver, this);
        /** 监听游戏暂停 */
        clientEvent.off(constant.EVENT_TYPE.ON_GAME_PAUSE, this._onGamePause, this);
        /** 监听刷新关卡 */
        clientEvent.off(constant.EVENT_TYPE.REFRESH_LEVEL, this._refreshLevel, this);
        /** 监听回收游戏模型、特效 */
        clientEvent.off(constant.EVENT_TYPE.RECYCLE_ALL, this._recycleAll, this);
    }




    /** 初始化游戏 */
    private _onGameInit() {
        // 获取当前关卡
        let level = playerData.instance.playerInfo.level;
        // 本地配置
        let totalLevel = localConfig.instance.getTableArr("checkpoint").length;
        //游戏通关后从倒数第10关开始循环(61-70)
        if (level > totalLevel) {
            level = (totalLevel - 10) + (level - totalLevel) % 10;
        }
        // 获取地图信息
        this.mapInfo = localConfig.instance.queryByID("checkpoint", String(level));

        //设置本层敌人属性加成比例
        /** 本层敌人攻击加成 */
        gameManager.attackAddition = this.mapInfo.attackAddition;
        /** 本层敌人防御加成 */
        gameManager.defenseAddition = this.mapInfo.defenseAddition;
        /** 本层敌人生命加成 */
        gameManager.hpAddition = this.mapInfo.hpAddition;
        /** 本层敌人移速加成 */
        gameManager.moveSpeedAddition = this.mapInfo.moveSpeedAddition;
        /** 本层敌人攻速加成 */
        gameManager.attackSpeedAddition = this.mapInfo.attackSpeedAddition;

        /** 显示单例界面(加载loading) */
        uiManager.instance.showDialog("loading/loadingPanel");

        /** 分发事件(隐藏传送门)*/
        clientEvent.dispatchEvent(constant.EVENT_TYPE.HIDE_WARP_GATE);

        // 初始化游戏设置
        /** 游戏是否开始 */
        gameManager.isGameStart = false;
        /** 游戏是否暂停 */
        gameManager.isGamePause = false;
        /** 游戏是否结束 */
        gameManager.isGameOver = false;
        /** 修改是否取得胜利 获取是否取得胜利 */
        gameManager.isWin = false;
        /** 玩家是否复活 */
        gameManager.isRevive = false;
        /** 小怪、boss数组 */
        gameManager.arrMonster = [];
        /** 获取移动速度 修改移动速度 */
        gameManager.gameSpeed = 1;
        /** 小怪节点 */
        gameManager.ndBoss = null!;
        /** 本层加载的npc、大爱心现存数量 怪物不会和npc、大爱心同时出现，配置表格需注意 */
        gameManager.existentNum = 0;

        /** 增加战斗次数 */
        playerData.instance.addFightTimes();

        /** 看广告时先将音乐暂停 */
        audioManager.instance.pauseAll();

        /** 刷新游戏 */
        this._refreshLevel()
    }

    /** 初始化游戏 */
    private _onGameOver() {
    }

    /** 暂停游戏 */
    private _onGamePause() {

    }

    /** 刷新游戏 */
    private _refreshLevel() {
        console.log('刷新游戏_refreshLevel')
        /** 获取全部地图 */
        let arrMap = this.mapInfo.mapName.split("#");
        /** 每层随机一张地图 */
        let mapName = arrMap[Math.floor(Math.random() * arrMap.length)];

        /** 每层进入前预加载该层所需的敌人技能 */
        this.preloadMonsterSkill(mapName).then(() => {
            /** 回收怪兽, 武器，特效等 */
            this._recycleAll();
            /** 加载地图数据(地图名称,加载完成回调) */
            this._loadMap(mapName, () => {
                /** 第一次进入或者失败后被销毁需要重新创建 */
                if (!gameManager.ndPlayer) {
                    /** 创建玩家 */
                    this._createPlayer();
                } else {
                    /** gameManager.玩家脚本.每次成功进入新的一层则更新玩家状态() */
                    gameManager.scriptPlayer.resetPlayerState();
                    /** 分发事件(隐藏加载界面) */
                    clientEvent.dispatchEvent(constant.EVENT_TYPE.HIDE_LOADING_PANEL);
                }
            });
        })
    }

    /**
     * 加载地图数据
     *
     * @private
     * @param {Function} [cb=()=>{}]
     * @memberof gameManager
     */
    private _loadMap(mapName: string, cb: Function = () => { }) {
        this.scriptMapManager.buildMap(mapName, () => { }, () => {
            cb && cb();
        })
        cb()
    }


    /** 回收怪兽, 武器，特效等 */
    private _recycleAll() {
        this.scriptMapManager.recycle();
        for (let i = this.node.children.length - 1; i >= 0; i--) {
            const ndChild = this.node.children[i];
            if (ndChild.name !== "player01") {
                poolManager.instance.putNode(ndChild);
            }
        }

        while (gameManager.ndEffectManager.children.length) {
            poolManager.instance.putNode(gameManager.ndEffectManager.children[0]);
        }
    }
    /**
     * 每层进入前预加载该层所需的敌人技能
     *
     * @private
     * @memberof gameManager
     */
    private preloadMonsterSkill(arrName: string) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(null)
            }, 1000)
        })

    }

    /**
     * 创建玩家
     *
     * @private
     * @memberof gameManager
     */
    private _createPlayer() {
        console.log('创建玩家_createPlayer')
        resourceUtil.loadModelRes("player/player01").then((pf: any) => {
            gameManager.ndPlayer = poolManager.instance.getNode(pf, this.node) as Node;

            let scriptGameCamera = gameManager.mainCamera?.node.getComponent(gameCamera) as gameCamera;
            scriptGameCamera.ndFollowTarget = gameManager.ndPlayer;

            let scriptPlayer = gameManager.ndPlayer?.getComponent(player) as player;
            gameManager.scriptPlayer = scriptPlayer;
            scriptPlayer?.init();

            //初次进入的时候需要预加载弓箭
            if (gameManager.isFirstLoad) {
                gameManager.isFirstLoad = false;

                scriptPlayer.preloadArrow(() => {
                    clientEvent.dispatchEvent(constant.EVENT_TYPE.HIDE_LOADING_PANEL);
                })
            } else {
                clientEvent.dispatchEvent(constant.EVENT_TYPE.HIDE_LOADING_PANEL);
            }

        })
    }



    /**
     * 刷新自节点的动画、粒子播放速度
     * @param targetNode
     * @param value
     * @returns
     */
    public static refreshEffectSpeed(targetNode: Node, value: number) {
        if (!targetNode) {
            return;
        }
        let arrAni = targetNode.getComponentsInChildren(AnimationComponent);
        arrAni.forEach((item: AnimationComponent) => {
            item.clips.forEach((clip: any) => {
                let aniName = clip?.name as string
                let aniState = item.getState(aniName);
                aniState.speed = value;
            })
        })

        let arrSkeletalAni = targetNode.getComponentsInChildren(SkeletalAnimationComponent);
        arrSkeletalAni.forEach((item: SkeletalAnimationComponent) => {
            item.clips.forEach((clip: any) => {
                let aniName = clip?.name as string
                let aniState = item.getState(aniName);
                aniState.speed = value;
            })
        })

        let arrParticle = targetNode.getComponentsInChildren(ParticleSystemComponent);
        arrParticle.forEach((item: ParticleSystemComponent) => {
            item.simulationSpeed = value;
        })
    }


    private _ndCloud: Node = null!;
    public buildMap(mapName: string, progressCb: Function, completeCb: Function) {
        this._completeListener = completeCb;
        this._dictModuleType = {};

        this._arrItem = [];
        this._arrMap = [];

        this._arrMap = localConfig.instance.getTableArr(mapName).concat();

        let cb = () => {
            if (mapName.startsWith("map1")) {
                this._ndAn && (this._ndAn.active = false);
                this._ndAnS && (this._ndAnS.active = true);
                mapManager.isMapAnS = true;
            } else {
                this._ndAn && (this._ndAn.active = true);
                this._ndAnS && (this._ndAnS.active = false);
                mapManager.isMapAnS = false;
            }

            for (let i = this._arrMap.length - 1; i >= 0; i--) {
                const item = this._arrMap[i];
                let baseInfo = localConfig.instance.queryByID('base', item.ID);

                if (!this._dictModuleType.hasOwnProperty(baseInfo.type)) {
                    this._dictModuleType[baseInfo.type] = [];
                }

                this._dictModuleType[baseInfo.type].push(item);
            }

            let arrPromise = [];

            for (const i in this._dictModuleType) {
                let item = this._dictModuleType[i];
                if (item.length) {
                    arrPromise.push(this._buildModel(i));
                }
            }

            Promise.all(arrPromise).then(() => {
                this._completeListener && this._completeListener();
                console.log(`load ${mapName} over`);
            }).catch((e) => {
                console.error("load item module err", e);
            })
        }

        if (!this._ndCloud) {
            resourceUtil.loadModelRes('scene/cloud').then((prefab: any) => {
                this._ndCloud = poolManager.instance.getNode(prefab, this.node.parent as Node);
            })
        }

        if (mapName.startsWith("map0") && !this._ndAn) {
            resourceUtil.loadModelRes('scene/an').then((prefab: any) => {
                this._ndAn = poolManager.instance.getNode(prefab, this.node.parent as Node);
                cb();
            })
        } else if (mapName.startsWith("map1") && !this._ndAnS) {
            resourceUtil.loadModelRes('scene/anS').then((prefab: any) => {
                this._ndAnS = poolManager.instance.getNode(prefab, this.node.parent as Node);
                cb();
            })
        } else {
            cb();
        }
    }

    private _buildModel(type: string) {
        return new Promise((resolve, reject) => {
            let arrPromise = [];

            let objItems = this._dictModuleType[type];//同类型的信息
            this._dictModuleType[type] = [];

            for (let idx = 0; idx < objItems.length; idx++) {
                //怪物在该层级别的配置信息
                let layerInfo = objItems[idx];
                //怪物的模块数据
                let baseInfo = localConfig.instance.queryByID("base", layerInfo.ID);
                let modelPath = `${type}/${baseInfo.resName}`;
                let p = resourceUtil.loadModelRes(modelPath).then((prefab: any) => {
                    let parentName = type + 'Group';//先创建父节点
                    let ndParent = this.node.getChildByName(parentName);

                    if (!ndParent) {
                        ndParent = new Node(parentName);
                        ndParent.parent = this.node;
                    }

                    let ndChild = poolManager.instance.getNode(prefab, ndParent) as Node;
                    let position = layerInfo.position ? layerInfo.position.split(',') : baseInfo.position.split(',');
                    let angle = layerInfo.angle ? layerInfo.angle.split(',') : baseInfo.angle.split(',');
                    let scale = layerInfo.scale ? layerInfo.scale.split(',') : baseInfo.scale.split(',');
                    ndChild.setPosition(new Vec3(Number(position[0]), Number(position[1]), Number(position[2])));
                    ndChild.eulerAngles = new Vec3(Number(angle[0]), Number(angle[1]), Number(angle[2]));
                    ndChild.setScale(new Vec3(Number(scale[0]), Number(scale[1]), Number(scale[2])));

                    //test
                    if (baseInfo.type === constant.BASE_TYPE.MONSTER) {
                        let scriptMonster = ndChild?.getComponent(monster) as monster;
                        scriptMonster.init(baseInfo, layerInfo);
                        gameManager.arrMonster.push(ndChild);
                    } else if (baseInfo.type === constant.BASE_TYPE.BOSS) {
                        gameManager.ndBoss = ndChild;
                        gameManager.scriptBoss = ndChild?.getComponent(boss) as boss;
                        gameManager.scriptBoss.init(baseInfo, layerInfo);
                    } else if (baseInfo.type === constant.BASE_TYPE.NPC) {
                        gameManager.existentNum += 1;
                    } else if (baseInfo.type === constant.BASE_TYPE.HEART) {
                        gameManager.existentNum += 1;
                    }

                    this._arrItem.push(ndChild);
                })

                arrPromise.push(p);
            }

            Promise.all(arrPromise).then(() => {
                resolve(null);
            }).catch((e) => {
                console.error("e", e);
            })
        })
    }


    start() {
        gameManager.mainCamera = this.camera?.getComponent(CameraComponent) as CameraComponent;
        gameManager.scriptGameCamera = this.camera?.getComponent(gameCamera) as gameCamera;
        gameManager.ndGameManager = this.node;
        gameManager.ndMapManager = find("mapManager") as Node;
        gameManager.ndEffectManager = find("effectManager") as Node;

        this._oriMainLightWorPos = this.ndLight.worldPosition.clone();

        if (gameManager.isTesting) {
            //@ts-ignore
            window.uiManager = uiManager.instance;
            //@ts-ignore
            window.audioManager = audioManager.instance;
            //@ts-ignore
            window.playerData = playerData.instance;
            //@ts-ignore
            window.clientEvent = clientEvent;
            //@ts-ignore
            window.ndGameManager = gameManager.ndGameManager;
            //@ts-ignore
            window.gameManager = gameManager;
            //@ts-ignoreUITransform
            window.ndMapManager = gameManager.ndMapManager;
            //@ts-ignore
            window.effectManager = effectManager.instance;
            //@ts-ignore
            window.ndEffectManager = gameManager.ndEffectManager;
            //@ts-ignore
            window.constant = constant;
            //@ts-ignore
            //@ts-ignore
        }
    }

    update(deltaTime: number) {
        //光源跟随玩家人物移动
        if (gameManager.scriptPlayer && gameManager.scriptPlayer.node && !gameManager.isGameOver) {
            Vec3.subtract(this._offsetWorPosMainLight, gameManager.ndPlayer.worldPosition, this._oriMainLightWorPos);
            this._offsetWorPosMainLight.set(this._offsetWorPosMainLight.x, 0, this._offsetWorPosMainLight.z);
            this.ndLight.setWorldPosition(this._offsetWorPosMainLight.add(this._oriMainLightWorPos));
        }
    }

    /**
     * 添加钻石
     *
     * @static
     * @param {number} [value=1]
     * @memberof gameManager
     */
    public static addGold(value: number = 1) {
        playerData.instance.updatePlayerInfo('gold', Math.ceil(value));
        clientEvent.dispatchEvent(constant.EVENT_TYPE.REFRESH_GOLD);
    }


    /**
     * 获取距离最近的小怪、boss节点
     * @returns 
     */
    public static getNearestMonster() {
        if (gameManager.arrMonster.length) {
            let offsetA: Vec3 = new Vec3();
            let offsetB: Vec3 = new Vec3();

            let playerWorPos = gameManager.ndPlayer?.worldPosition as Vec3;
            let arr = gameManager.arrMonster.sort((a: any, b: any) => {
                let distanceA = Vec3.subtract(offsetA, playerWorPos, a.worldPosition).length();
                let distanceB = Vec3.subtract(offsetB, playerWorPos, b.worldPosition).length();
                return distanceA - distanceB;
            })

            arr = arr.filter((item: Node) => {
                let scriptMonster = item.getComponent(monster) as monster;
                return item.parent !== null && !scriptMonster.isDie;
            })

            return arr[0];
        } else if (gameManager.ndBoss && !gameManager.scriptBoss.isDie) {
            return gameManager.ndBoss;
        } else {
            return null;
        }
    }
}


